{"componentChunkName":"component---node-modules-gatsby-theme-chronoblog-src-templates-post-js","path":"/nightwatch-page-object-model-with-commands/","result":{"data":{"mdx":{"id":"231a51bf-7009-5b04-872c-1482953085f6","excerpt":"Nightwatch.js page object model and commands API The page object model in test automation allows you as a test engineer to write more…","frontmatter":{"title":"Writing maintainable tests with Nightwatch.js page objects and commands","date":"2020-04-26T00:00:00.000Z","description":"Using page objects and commands for repetitive selectors and test code allows you to write maintainable automated test cases. Learn how in this Nightwatch automation tutorial.","tags":["quality assurance","software testing","nightwatchjs","post"],"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAYHAgX/xAAUAQEAAAAAAAAAAAAAAAAAAAAE/9oADAMBAAIQAxAAAAGAdTDgJEXGMQf/xAAbEAACAwADAAAAAAAAAAAAAAADBAECBRESIf/aAAgBAQABBQJHPM6uDEtcV+e63mMGIkzKooP/AP/EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAwEBPwGNf//EABcRAQEBAQAAAAAAAAAAAAAAAAEAAhH/2gAIAQIBAT8B08Y2X//EACIQAAEDAwMFAAAAAAAAAAAAAAIAAREDEiExQVEEIjKhwf/aAAgBAQAGPwIqoWiI4a99fSYqnUiBO3gwTE8p7tVRjk1EYx8R9u77r//EABwQAQACAgMBAAAAAAAAAAAAAAEAESExQVFhcf/aAAgBAQABPyEOOOG9cQbs7ST0IxmlpmfSLZT7FTFDD6wSFfZ2z//aAAwDAQACAAMAAAAQ4w//xAAWEQEBAQAAAAAAAAAAAAAAAAABIQD/2gAIAQMBAT8QoF1Xf//EABcRAQEBAQAAAAAAAAAAAAAAAAERAEH/2gAIAQIBAT8QoAd0ZDf/xAAZEAEBAQEBAQAAAAAAAAAAAAABEQAhMcH/2gAIAQEAAT8Q70xgR9BrnhVCp3exXMQEKeUGFFzaqusSIyaqIoQs5zkjRATq0zTw+lv/2Q==","aspectRatio":1.761467889908257,"src":"/static/66f0a129b389ddbfb6f2d718b126bc76/3e61c/nightwatch-page-object-model-testing.jpg","srcSet":"/static/66f0a129b389ddbfb6f2d718b126bc76/7fcb3/nightwatch-page-object-model-testing.jpg 192w,\n/static/66f0a129b389ddbfb6f2d718b126bc76/42594/nightwatch-page-object-model-testing.jpg 384w,\n/static/66f0a129b389ddbfb6f2d718b126bc76/3e61c/nightwatch-page-object-model-testing.jpg 768w,\n/static/66f0a129b389ddbfb6f2d718b126bc76/d66c9/nightwatch-page-object-model-testing.jpg 837w","srcWebp":"/static/66f0a129b389ddbfb6f2d718b126bc76/dd090/nightwatch-page-object-model-testing.webp","srcSetWebp":"/static/66f0a129b389ddbfb6f2d718b126bc76/ae504/nightwatch-page-object-model-testing.webp 192w,\n/static/66f0a129b389ddbfb6f2d718b126bc76/fef30/nightwatch-page-object-model-testing.webp 384w,\n/static/66f0a129b389ddbfb6f2d718b126bc76/dd090/nightwatch-page-object-model-testing.webp 768w,\n/static/66f0a129b389ddbfb6f2d718b126bc76/51deb/nightwatch-page-object-model-testing.webp 837w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":434},"resize":{"src":"/static/66f0a129b389ddbfb6f2d718b126bc76/3e61c/nightwatch-page-object-model-testing.jpg"}}}},"fields":{"slug":"/nightwatch-page-object-model-with-commands/"},"body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Writing maintainable tests with Nightwatch.js page objects and commands\",\n  \"cover\": \"./nightwatch-page-object-model-testing.jpg\",\n  \"date\": \"2020-04-26T00:00:00.000Z\",\n  \"description\": \"Using page objects and commands for repetitive selectors and test code allows you to write maintainable automated test cases. Learn how in this Nightwatch automation tutorial.\",\n  \"tags\": [\"quality assurance\", \"software testing\", \"nightwatchjs\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"nightwatchjs-page-object-model-and-commands-api\"\n  }, \"Nightwatch.js page object model and commands API\"), mdx(\"p\", null, \"The page object model in test automation allows you as a test engineer to write more readable and maintainable tests by placing the CSS or XPath selectors for the DOM elements into a single organized object. \"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"The page object centralizes common selectors and logic for portions of the UI relevant to your tests. This page object can expose functions that hold repetitive logic so you don't need to repeat the same code all over your test classes.\")), mdx(\"p\", null, \"Tests become more readable because\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"There is less code overall (less repetitive code)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Selectors can change from something like \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \".click('div select option:checked')\"), \" to \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \".click('@friendlyName')\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"If the UI changes you only need to make the change in one place to fix all your tests\")), mdx(\"p\", null, \"Well-written test automation frameworks like Nightwatch.js support this using their Page Object API which we will cover in this tutorial.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/gJvAbGYP-H8\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"refactoring-tests-to-use-the-page-object-model\"\n  }, \"Refactoring tests to use the page object model\"), mdx(\"p\", null, \"The example software under test, SUT, in this course is a simple calculator. It supports addition, subtraction, multiplication, and division of two integers selectable through drop down select elements. The integer range is 0 to 5. Let's also assume for example sake say we need to test this at the UI layer.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"500px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"47.80487804878049%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABJ0AAASdAHeZh94AAABXUlEQVQoz51S207CQBDt//+IUMoLkJL64CWACQgK0S29LjQUX2ixoC0PvRy3xa0ViYlOMpnZnZkzc2ZXIIRgNn1Ev9/H5GGMTruF3m0Pw/sxPM+Ds1xiNBpCM0wQ8oTpbIrxZAKb2siyDLlwm4ugKAqkhgip2YTclVGvXUCs1dFVLqGqKm6uryCKIospkOUOWu02GpKEweCuBPsGmCRJGUg/A2maIYljJEnK/PRYUCnikp25E3TdgMHo2LYNd/0CyzTwTNRiOsdZwaIUmjZnvsMaomjAlU9XpS7gD1Kd6BSoBKxSPpdU+Jw2jsxPgatWME0LlmWB0gV0XYfrrrBau1gwqnmMMkvZOgxdg8HyFstjnm1TzJklKkGw239NGIURoijC4XBAGIZ4Z8rPcfEwSaFVn5/5Xb7PEnCz2SAIAuxYF9/34Pk+Xrdb7Pdv+M9+hZ9/KStf8XS3vynP/QDkNvjp5dBPDwAAAABJRU5ErkJggg==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Calculator\",\n    \"title\": \"Calculator\",\n    \"src\": \"/static/3f721583bf04f7011123ff7ca89014c2/c6e3d/mello-calculator.png\",\n    \"srcSet\": [\"/static/3f721583bf04f7011123ff7ca89014c2/ad4a5/mello-calculator.png 205w\", \"/static/3f721583bf04f7011123ff7ca89014c2/74ab4/mello-calculator.png 410w\", \"/static/3f721583bf04f7011123ff7ca89014c2/c6e3d/mello-calculator.png 500w\"],\n    \"sizes\": \"(max-width: 500px) 100vw, 500px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"A basic automated test for this might be to test adding 2+2=4 and look like the example below.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'two plus two is four': function (browser) {\\n        browser\\n            .url('http://localhost:3000')\\n            .setValue('#numList1', 2)\\n            .setValue('#operatorList', '+')\\n            .setValue('#numList2', 2)\\n            .click('#submit')\\n            .expect.element('#result').text.to.equal('4');\\n    }\\n\")), mdx(\"p\", null, \"One can imagine if we were to exhaustively test the calculator we could have 144 tests (6 integers \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"*\"), \" 6 integers \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"*\"), \" 4 math operations) plus any other business or UI requirements outside basic math. \"), mdx(\"p\", null, \"This would result in a large amount of tests with the same hardcoded CSS selectors and value setting logic.\"), mdx(\"p\", null, \"If the page interface changed in the future we'd have a test maintenance nightmare \\uD83D\\uDE30\"), mdx(\"h3\", {\n    \"id\": \"lets-refactor-to-use-page-objects\"\n  }, \"Let's refactor to use page objects\"), mdx(\"p\", null, \"Leveraging the Nightwatch Page Object API we can refactor the above test case to this example below.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'two plus two is four': function (browser) {\\n        let calcPage = browser.page.calculator();\\n\\n        calcPage\\n            .setValue('@firstNumber', 2)\\n            .setValue('@operator', '+')\\n            .setValue('@secondNumber', 2)\\n            .click('@submit')\\n            .expect.element('@result').text.to.equal('4');\\n    }\\n\")), mdx(\"p\", null, \"The CSS DOM selectors are moved to the page object called calculator.js. The CSS selectors in the test are replaced using \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"@elementNameHere\"), \" placeholders/pointers to the name of the property in the element collection and Nightwatch resolves it to the CSS selector defined in the page object module when the test runs.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"module.exports = {\\n    url: 'http://localhost:3000',\\n    elements: {\\n        firstNumber: '#numList1',\\n        secondNumber: '#numList2',\\n        operator: '#operatorList',\\n        result: '#result',\\n        submit: '#submit'\\n    }\\n}\\n\")), mdx(\"p\", null, \"So now if the selectors on the page ever change you can just make the change in one place, the page object, calculator.js.\"), mdx(\"p\", null, \"This is better, but we still have a lot of repetitive logic here for setting the value...\"), mdx(\"h3\", {\n    \"id\": \"lets-refactor-to-use-page-object-commands\"\n  }, \"Let's refactor to use page object commands\"), mdx(\"p\", null, \"We can make the tests use less repetitive code by refactoring it into a command in the page object. \"), mdx(\"p\", null, \"As a bonus, by naming it something descriptive like \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"add(...)\"), \" it makes the intention of the test more understandable instead of a series of clicks and setValue.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'two plus two is four': function (browser) {\\n        browser.page.calculator()\\n            .add(2, 2)\\n            .expect.element('@result').text.to.equal('4');\\n    }\\n\")), mdx(\"p\", null, \"Considerably more readable and descriptive! \\uD83D\\uDE0C\"), mdx(\"p\", null, \"The logic for \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \".add(...)\"), \" gets added to the calculator page object and the object containing the custom commands gets wired into the page object by adding it to the \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"commands:\"), \" property as shown toward the bottom.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"const calculatorCommands = {\\n    add: function (n1, n2) {\\n        return this.fillAndSubmit(n1, n2, '+');\\n    },\\n    fillAndSubmit: function (n1, n2, op) {\\n        return this\\n            .setValue('@operator', op)\\n            .setValue('@firstNumber', n1)\\n            .setValue('@secondNumber', n2)\\n            .click('@submit');\\n    }\\n    \\n...\\n\\nmodule.exports = {\\n    url: 'http://localhost:3000',\\n    commands: [calculatorCommands],\\n    elements: {\\n    \\n    ...\\n\")), mdx(\"p\", null, \"Moving the common logic to one place instead of scattered in all your test cases has the same advantage of selectors if the functionality of the page changes in the future. For example, maybe there is navigation to the page changes or we change from a drop down to a text box. Fixing the tests only has to be done in one place.\"), mdx(\"h3\", {\n    \"id\": \"further-refactoring\"\n  }, \"Further refactoring?\"), mdx(\"p\", null, \"So now we have something readable and maintainable like these series of tests.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"    '0-5=-5': function (browser) {\\n        browser.page.calculator()\\n            .subtract(0, 5)\\n            .expect.element('@result')\\n            .text.to.equal('-5');\\n    },\\n    '2-2=0': function (browser) {\\n        browser.page.calculator()\\n            .subtract(2, 2)\\n            .expect.element('@result')\\n            .text.to.equal('0');\\n    },\\n    '5*2=10': function (browser) {\\n        browser.page.calculator()\\n            .multiply(5, 2)\\n            .expect.element('@result')\\n            .text.to.equal('10');\\n    },\\n    '5 divided by 0 is Infinity': function (browser) {\\n        browser.page.calculator()\\n            .divide(5, 0)\\n            .expect.element('@result')\\n            .text.to.equal('Infinity');\\n    }\\n\")), mdx(\"p\", null, \"You could further optimize as a matter of preference to something like the below example too.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"    '0-5=-5': function (browser) {\\n        browser.page.calculator()\\n            .subtracting(5).from(0).equals(5)\\n    },\\n    '5 divided by 0 is Infinity': function (browser) {\\n        browser.page.calculator()\\n            .dividing(5).by(0).equals('Infinity')\\n    }\\n\")), mdx(\"h2\", {\n    \"id\": \"configuring-nightwatchjs-to-use-page-objects\"\n  }, \"Configuring Nightwatch.js to use page objects\"), mdx(\"p\", null, \"To allow Nightwatch to find your page objects you need to add the page_objects_path to your nightwatch.json file. My example puts it in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"./page-objects/\"), \" and my tests themselves are in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"./tests/\")), mdx(\"p\", null, \"nightwatch.json\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-json\"\n  }, \"{\\n    \\\"src_folders\\\": [\\\"test\\\"],\\n    \\\"page_objects_path\\\": \\\"page-objects\\\",\\n\\n    \\\"selenium\\\": {\\n        \\\"start_process\\\": false\\n    },\\n\\n    \\\"webdriver\\\": {\\n        \\\"start_process\\\": true,\\n        \\\"server_path\\\": \\\"node_modules/chromedriver/lib/chromedriver/chromedriver\\\",\\n        \\\"port\\\": 9515\\n    },\\n\\n    \\\"test_settings\\\": {\\n        \\\"default\\\": {\\n            \\\"desiredCapabilities\\\": {\\n                \\\"browserName\\\": \\\"chrome\\\"\\n            }\\n        }\\n    }\\n}\\n\")), mdx(\"p\", null, \"Nightwatch expects the page object at that path to be a .js file. The filename you give it will be how it is referenced in your test through the Nightwatch browser object passed through your tests.\"), mdx(\"p\", null, \"For example, you can access the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\\page-objects\\\\calculator.js\"), \" page object in your test with syntax\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'0-5=-5': function (browser) {\\n        browser.page.calculator().etc.etc;\\n}\\n\")), mdx(\"p\", null, \"I've posted the entire example for testing my calculator application on my GitHub\\n\\uD83D\\uDC49 \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/tree/master/dropDowns\"\n  }, \"Nightwatch.js page objects and drop down select elements tutorial\")), mdx(\"p\", null, \"If you are a Nightwatch beginner and haven't seen my introduction on setting up your Nightwatch project and running tests you'll enjoy the tutorial below\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/6Ufg6pPNVTs\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"If you have any questions or comments please reach out through my social links below \\uD83D\\uDC47\"));\n}\n;\nMDXContent.isMDXComponent = true;"}},"pageContext":{"id":"231a51bf-7009-5b04-872c-1482953085f6"}},"staticQueryHashes":["1961101537","2542493696"]}